//
//  SegmentView.swift
//  ContactPoster
//
//  Created by TSYH on 2024/1/26.
//

import UIKit
import ExtensionsKit

public protocol SegmentViewDataSource: NSObjectProtocol {
    func segmentIndicator(_ segmentView: SegmentView) -> UIView
    func segmentBackgroundColor(_ segmentView: SegmentView) -> UIColor?
    func segmentInset(_ segmentView: SegmentView) -> UIEdgeInsets
    func segmentItems(_ segmentView: SegmentView) -> [SegmentViewItemProtocol]
    
    func segmentAlignment(_ segmentView: SegmentView) -> SegmentAlignment
    func segmentContentMode(_ segmentView: SegmentView) -> SegmentContentMode
    func segment(_ segmentView: SegmentView, didSelected item: SegmentViewItemProtocol)
}

public extension SegmentViewDataSource {
    func segmentInset(_ segmentView: SegmentView) -> UIEdgeInsets {
        return .zero
    }
    
    func segmentBackgroundColor(_ segmentView: SegmentView) -> UIColor? {
        return UIColor.white.withAlphaComponent(0.1)
    }
    
    func segmentAlignment(_ segmentView: SegmentView) -> SegmentAlignment {
        return .center
    }
}

public protocol SegmentViewItemProtocol {
    var segmetTitle: NSAttributedString? { get }
    var segmetSelectedTitle: NSAttributedString? { get }
}

public enum SegmentAlignment {
    case fill(UIEdgeInsets)
    case leading
    case center
    case trailing
    
    
    var inset: UIEdgeInsets {
        if case let .fill(value) = self {
            return value
        }
        return .zero
    }
}

public enum SegmentContentMode {
    /// 等宽
    case equalWidth(CGFloat)
    
    /// 等间距，宽度自适应
    case equalSpacing(CGFloat)
    
    var itemWidth: CGFloat? {
        switch self {
        case let .equalWidth(value):
            return value
        default:
            return nil
        }
    }
    
    var spacing: CGFloat {
        switch self {
        case let .equalSpacing(value):
            return value
        default:
            return 0
        }
    }
}

public class SegmentView: UIView {
    lazy var bgView = UIView()
    lazy var scrollView = UIScrollView()
    lazy var stackView = UIStackView()
    lazy var indicator: UIView = UIView()
    
    public weak var dataSource: SegmentViewDataSource?
    
    lazy var segmentItems = [SegmentViewItemProtocol]()
    public func reload() {
        guard let dataSource = dataSource else { return }
        stackView.arrangedSubviews.forEach({ $0.removeFromSuperview() })
        segmentItems.removeAll()
        
        indicator = dataSource.segmentIndicator(self)
        scrollView.insertSubview(indicator, at: 0)
        
        if let bgColor = dataSource.segmentBackgroundColor(self) {
            bgView.backgroundColor = bgColor
            bgView.cornerRadius = 14
        }
        
        let mode = dataSource.segmentContentMode(self)
        stackView.spacing = mode.spacing
        
        let alignment = dataSource.segmentAlignment(self)
        stackView.snp.remakeConstraints { make in
            make.centerY.equalToSuperview()
            make.height.equalToSuperview()
            switch alignment {
            case let .fill(inset):
                make.leading.equalTo(inset.left)
                make.trailing.equalTo(-inset.right)
            case .leading:
                make.leading.equalToSuperview()
            case .center:
                make.centerX.equalToSuperview()
            case .trailing:
                make.trailing.equalToSuperview()
            }
        }
        
        let items = dataSource.segmentItems(self)
        self.segmentItems = items
        
        var indicatorWidth: CGFloat = 0
        for (index, item) in items.enumerated() {
            let button = UIButton()
            button.setAttributedTitle(item.segmetTitle, for: .normal)
            button.setAttributedTitle(item.segmetSelectedTitle, for: .selected)
            button.setTitleColor(.white, for: .normal)
            button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
            button.addTarget(self, action: #selector(buttonClick(_:)), for: .touchUpInside)
            button.tag = 100+index
            button.sizeToFit()
            
//            button.backgroundColor = UIColor.red.withAlphaComponent(0.3)
            stackView.addArrangedSubview(button)
            
            if let itemWidth = mode.itemWidth {
                button.snp.makeConstraints { make in
                    make.width.equalTo(itemWidth)
                }
                indicatorWidth = itemWidth
            } else {
                if indicatorWidth == 0 {
                    indicatorWidth = button.width
                }
            }
        }
        currentIndex = 0
        
        setNeedsLayout()
        
        DispatchQueue.main.asyncAfter(deadline: .now()+0.1) {
            if let first = self.stackView.arrangedSubviews.first {
                self.indicator.frame = CGRect(x: 0, y: 0, width: first.width+self.stackView.spacing, height: self.indicator.height)
                self.indicator.centerX = self.stackView.convert(first.center, to: self.scrollView).x
            }
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        addSubview(bgView)
        bgView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        
        scrollView.showsHorizontalScrollIndicator = false
        bgView.addSubview(scrollView)
        scrollView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        
//        stackView.backgroundColor = UIColor.red.withAlphaComponent(0.3)
        stackView.axis = .horizontal
        stackView.alignment = .center
        stackView.distribution = .fill
        scrollView.addSubview(stackView)
        stackView.snp.makeConstraints { make in
            make.trailing.equalToSuperview()
            make.leading.trailing.equalToSuperview()
            make.centerY.equalToSuperview()
            make.height.equalToSuperview()
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func layoutSubviews() {
        super.layoutSubviews()
        indicator.height = bgView.height
        indicator.cornerRadius = indicator.height/2.0
    }
    
    lazy var currentIndex: Int = 0 {
        didSet {
            // 选中状态
            stackView.arrangedSubviews.forEach({ ($0 as? UIButton)?.isSelected = $0.tag == currentIndex+100 })
        }
    }
    
    @objc func buttonClick(_ sender: UIButton) {
        moveIndicatorCenter(to: sender)
        if let item = segmentItems.value(at: sender.tag-100) {
            dataSource?.segment(self, didSelected: item)
        }
    }
    
    var moveByTap: Bool = false
    func moveIndicatorCenter(to sender: UIView) {
        moveByTap = true
        
        let targetWidth = sender.width+stackView.spacing
        let targetCenterX = stackView.convert(sender.center, to: scrollView).x
        let x = targetCenterX - targetWidth/2.0
        
        UIView.animate(withDuration: 0.25) {
            self.indicator.frame = CGRect(x: x, y: 0, width: targetWidth, height: self.indicator.height)
        } completion: { finished in
            if finished {
                self.moveByTap = false
                self.scrollToCenter()
                self.currentIndex = sender.tag-100
            }
        }
    }
    
    // 滚到屏幕中央
    func scrollToCenter() {
        let maxOffsetX = scrollView.contentSize.width-scrollView.width
        guard maxOffsetX > 0 else { return }
        
        let indicatorCenter = indicator.centerX
        let offsetX = max(0, min(maxOffsetX, indicatorCenter - scrollView.width/2.0))
        scrollView.setContentOffset(CGPoint(x: offsetX, y: 0), animated: true)
    }
    
    public func moveIndicator(scrollView: UIScrollView) {
        guard !moveByTap, scrollView.contentSize.width > 0, scrollView.width > 0 else { return }
        
        let ratio = scrollView.contentOffset.x/scrollView.contentSize.width
        let page = scrollView.contentSize.width/scrollView.width
        let value = ratio*page
        let offsetRatio = value - value.rounded(.down)
        let index = Int(value.rounded(.down))
        guard let leftItem = stackView.viewWithTag(index+100),
              let rightItem = stackView.viewWithTag(index+1+100) else {
            return
        }
        // 中心距离
        let distance = abs(leftItem.centerX - rightItem.centerX)
        let leftCenterX = stackView.convert(leftItem.center, to: self.scrollView).x
        
        let extentWidth = rightItem.width - leftItem.width
        let targetWidth = leftItem.width + stackView.spacing + extentWidth*offsetRatio
        
        // 最终位置为左边 + 偏移比例
        indicator.frame = CGRect(x: 0, y: 0, width: targetWidth, height: indicator.height)
        if semanticContentAttribute == .forceRightToLeft {
            indicator.centerX = leftCenterX - distance*offsetRatio
        } else {
            indicator.centerX = leftCenterX + distance*offsetRatio
        }
        if !scrollView.isTracking {
            scrollToCenter()
            let index = (scrollView.contentOffset.x/scrollView.width).rounded()
            currentIndex = Int(index)
        }
    }
    
    func moveIndicator(to index: Int) {
        guard let item = stackView.arrangedSubviews.value(at: index) else {
            return
        }
        moveIndicatorCenter(to: item)
    }
}
